#include "env.hpp"
#include "ast/ast.hpp"
#include "runtime/object.hpp"
#include "runtime/klass.hpp"
#include "runtime/heap.hpp"

#include <stdio.h>

StringObject* BoolObject::TrueValue = new StringObject("True");
StringObject* BoolObject::FalseValue = new StringObject("False");

IntObject* UnitObject::UnitValue = new IntObject(0);

Object* Object::add(Object* o) {
    printf("Unexpected type for '+'\n");
    exit(1);
}

Object* Object::mul(Object* o) {
    printf("Unexpected type for '*'\n");
    exit(1);
}

bool Object::is_char() { 
    return _klass == CharKlass::get_instance(); 
};

bool Object::is_int() { 
    return _klass == IntKlass::get_instance(); 
};

bool Object::is_native_func() { 
    return _klass == NativeFunctionKlass::get_instance(); 
};

/*
 * IntObject
 */
IntObject::IntObject(int v) : _value(v) {
    set_klass(IntKlass::get_instance());
}

void IntObject::print() {
    printf("%d", _value);
}

bool IntObject::equal(IntObject* y) {
    return _value == y->value();
}

Object* IntObject::add(Object* r) {
    IntObject* ir = (IntObject*)r;
    return new IntObject(_value + ir->value());
}

IntObject* IntObject::bit_and(IntObject* r) {
    return new IntObject(_value & r->value());
}

IntObject* IntObject::bit_or(IntObject* r) {
    return new IntObject(_value | r->value());
}

IntObject* IntObject::bit_xor(IntObject* r) {
    return new IntObject(_value ^ r->value());
}

IntObject* IntObject::lshift(IntObject* r) {
    return new IntObject(_value << r->value());
}

IntObject* IntObject::rshift(IntObject* r) {
    return new IntObject(_value >> r->value());
}

IntObject* IntObject::sub(IntObject* r) {
    return new IntObject(_value - r->value());
}

Object* IntObject::mul(Object* r) {
    IntObject* io = (IntObject*)r;
    return new IntObject(_value * io->value());
}

IntObject* IntObject::div(IntObject* r) {
    return new IntObject(_value / r->value());
}

void* Object::operator new(size_t size) {
    return Heap::get_instance()->allocate(size);
}

/*
 *  String Object.
 */
StringObject::StringObject(const char* s, int len) {
    _length = len + 1;
    if (len == 0) {
        _value = NULL;
        return;
    }
    _value = (char*)Heap::get_instance()->allocate(_length);
    strncpy(_value, s, len);
    _value[len] = '\0';
    set_klass(StringKlass::get_instance());
}

StringObject::StringObject(const char* s) {
    _length = strlen(s) + 1;
    _value = (char*)Heap::get_instance()->allocate(_length);
    strncpy(_value, s, _length - 1);
    _value[_length-1] = '\0';
    set_klass(StringKlass::get_instance());
}

void StringObject::print() {
    printf("%s", _value);
}

Object* StringObject::add(Object* o) {
    StringObject* so = (StringObject*)o;
    char* s = (char*)Heap::get_instance()->allocate(_length + so->length() - 1);

    strncpy(s, _value, _length-1);
    strncat(s, so->value(), so->length() - 1);

    StringObject* to = new StringObject(NULL, 0);
    to->set_value(s);
    to->set_length(_length + so->length() - 1);

    return to;
}

Object* StringObject::mul(Object* o) {
    IntObject* io = (IntObject*)o;
    size_t new_len = (_length - 1) * io->value() + 1;
    char* s = (char*)Heap::get_instance()->allocate(new_len);

    strncpy(s, _value, _length-1);
    for (int i = 1; i < io->value(); i++) {
        strncat(s, _value, _length-1);
    }

    StringObject* to = new StringObject(NULL, 0);
    to->set_value(s);
    to->set_length(new_len);

    return to;
}

/*
 * CharObject
 */
CharObject::CharObject(char v) : _value(v) {
    set_klass(CharKlass::get_instance());
}

void CharObject::print() {
    printf("%c", _value);
}

Object* CharObject::add(Object* r) {
    CharObject* co = (CharObject*) r;
    return new CharObject(_value + co->value());
}

CharObject* CharObject::sub(CharObject* r) {
    return new CharObject(_value - r->value());
}

int Object::compare(Object* o) {
    return 0;
}

int IntObject::compare(Object* o) {
    return _value - ((IntObject*)o)->value();
}

int StringObject::compare(Object* o) {
    const char* d = ((StringObject*)o)->value();
    const char* s = value();
    
    while (*s != 0 && *d != 0) {
        if (*s == *d) {
            s++;
            d++;
        }
        else if (*s < *d) {
            return -1;
        }
        else {
            return 1;
        }
    }
    
    if (*s == 0 && *d == 0) {
        return 0;
    }
    else if (*s == 0) {
        return -1;
    }
    else {
        return 1;
    }
}

/*
 * Functions
 */
ClosureObject::ClosureObject(LambdaDef* l, DictObject* e): _lambda(l), _env(e) {
    set_klass(ClosureKlass::get_instance());
}

void ClosureObject::print() {
    printf("lambda_def");
}

